﻿namespace MSSqlSchemaDoc.Provider.MicrosoftSqlServer
{
    using System.Collections.ObjectModel;
    using System.Data;
    using System.IO;
    using MSSqlSchemaDoc.Core;
    using MSSqlSchemaDoc.Core.DataStructures;
    using MSSqlSchemaDoc.Provider.MicrosoftSqlServer.Helpers;

    /// <summary>
    /// Contains common scripting helper methods.
    /// </summary>
    public partial class SqlScriptingManager
    {
        /// <summary>
        /// Construct a parameter from a data reader.
        /// </summary>
        /// <param name="reader">Use the data in the current position of the data reader.</param>
        /// <returns>The constructed parameter.</returns>
        private static ProcedureParameter ConstructProcedureParameter(IDataReader reader)
        {
            ProcedureParameter p = new ProcedureParameter();
            p.Name = DataReaderHelper.GetString(reader, "ParameterName");

            if (string.IsNullOrEmpty(p.Name))
            {
                p.Name = "OUTPUT";
            }

            p.HasDefaultValue = DataReaderHelper.GetBool(reader, "HasDefaultValue");
            if (p.HasDefaultValue)
            {
                p.DefaultValue = DataReaderHelper.GetString(reader, "DefaultValue");
            }

            p.IsCompleteXmlDocument = DataReaderHelper.GetBool(reader, "IsCompleteXmlDocument");
            p.IsCursorReference = DataReaderHelper.GetBool(reader, "IsCursorReference");
            p.IsOutputParameter = DataReaderHelper.GetBool(reader, "IsOutputParameter");
            p.XmlCollectionId = DataReaderHelper.GetInt(reader, "XmlCollectionId");

            p.DataType = new DataType();
            PropertyBinder.CopyValues(reader, p.DataType, "Name", "TypeName");

            return p;
        }

        /// <summary>
        /// Retrievies the documentation from an existing object out of a collection.
        /// </summary>
        /// <typeparam name="T">The documentation object type.</typeparam>
        /// <param name="collection">The collection containing the existing documentation.</param>
        /// <param name="name">The name of the object to search for.</param>
        /// <param name="schema">The schema of the object to search for (optional).</param>
        /// <returns>The documentation text of the existing documentation object, or string.Empty if it is not found.</returns>
        private static string GetDocumentationFromCollection<T>(Collection<T> collection, string name, string schema)
            where T : IDocumentationObject
        {
            foreach (T d in collection)
            {
                if (d.Name == name && (d.Schema == schema || string.IsNullOrEmpty(schema)))
                {
                    return d.Documentation;
                }
            }

            return string.Empty;
        }

        /// <summary>
        /// Attempts to de-serialize an existing documentation object. Creates a new documentation object if deserialization fails.
        /// </summary>
        /// <typeparam name="T">The type of documentation object to deserialize.</typeparam>
        /// <param name="documentation">The new documentation object. The name (and possible schema) will be used to ascertain what the name of the documentation object is.</param>
        /// <returns>The deserialized documentation object.</returns>
        private T GetDocumentationObject<T>(IDocumentationObject documentation)
            where T : class, new()
        {
            FileInfo existingDocumentationFile = ScriptingManagerBase.GetFileInfo(this.DocumentationSourceFolder, documentation.ToString(), documentation.ObjectType.ToString(), this.CreateSubDirectories);
            T existingDocumentation = XmlSerializer.DeserializeDataStructureObject(typeof(T), existingDocumentationFile.FullName) as T;

            if (existingDocumentation == null)
            {
                existingDocumentation = new T();
            }

            return existingDocumentation;
        }

        /// <summary>
        /// Constructs a documentation object from an existing documentation object.
        /// </summary>
        /// <typeparam name="T">The documentation object type.</typeparam>
        /// <param name="name">The name of the documentation object.</param>
        /// <param name="schema">The schema of the documentation object (optional).</param>
        /// <returns>The new documentation object with the top level documentation copied from the existing documentation object.</returns>
        private T ConstructDocumentationObject<T>(string name, string schema)
            where T : class, IDocumentationObject, new()
        {
            T existingDocumentation;
            return this.ConstructDocumentationObject<T>(out existingDocumentation, name, schema);
        }

        /// <summary>
        /// Constructs a documentation object from an existing documentation object.
        /// </summary>
        /// <typeparam name="T">The documentation object type.</typeparam>
        /// <param name="existingDocumentation">This will be the existing documentation object (if it exists).</param>
        /// <param name="name">The name of the documentation object.</param>
        /// <param name="schema">The schema of the documentation object (optional).</param>
        /// <returns>The new documentation object with the top level documentation copied from the existing documentation object.</returns>
        private T ConstructDocumentationObject<T>(out T existingDocumentation, string name, string schema)
            where T : class, IDocumentationObject, new()
        {
            T documentation = new T() { Name = name };

            if (!string.IsNullOrEmpty(schema))
            {
                // Use both name AND schema
                documentation.Schema = schema;
            }

            existingDocumentation = GetDocumentationObject<T>(documentation);

            if (!string.IsNullOrEmpty(existingDocumentation.Documentation))
            {
                documentation.Documentation = existingDocumentation.Documentation;
            }
            else
            {
                documentation.Documentation = string.Empty;
            }

            return documentation;
        }
    }
}
